Skip to content

gh-148587: Make sys.lazy_modules immutable#149408

Draft
johnslavik wants to merge 13 commits intopython:mainfrom
johnslavik:gh-148587-revamp-sys-lazy-modules
Draft

gh-148587: Make sys.lazy_modules immutable#149408
johnslavik wants to merge 13 commits intopython:mainfrom
johnslavik:gh-148587-revamp-sys-lazy-modules

Conversation

@johnslavik
Copy link
Copy Markdown
Member

@johnslavik johnslavik commented May 5, 2026

Feel free to chime in with criticism. I'm not sure this is it yet, but take it in your own hands if you want.

Expose ``sys.lazy_modules`` via ``sys.__getattr__`` (PEP 562) as a
``frozendict[str, frozenset[str]]`` snapshot rebuilt on each access. The
live registry is kept private to the import system, preventing user code
from mutating it. Add ``_imp._clear_lazy_modules()`` for the test suite
to reset registry state between tests.
The live mutable registry of lazy imports is exposed (undocumented) as
``sys._lazy_modules``. ``sys.lazy_modules`` remains an immutable
``frozendict`` snapshot rebuilt on each access via ``sys.__getattr__``.

Tests reset state with ``sys._lazy_modules.clear()``; the temporary
``_imp._clear_lazy_modules()`` helper is removed.
- Reword NEWS to describe contract (read-only frozendict mapping module
  names to frozensets of submodule names) rather than implementation
  details. Match PEP 810 wording ("mapping").
- Drop the sys.__getattr__ docstring and restore the borrowed-reference
  comment in _PySys_Create.
- Use "mapping" in test docstrings/comments that describe the contract
  while leaving "snapshot" in implementation-detail spots.
Drop test_lazy_modules_is_immutable; the frozendict assertion already
covers immutability. Trim the trailing sentence in the NEWS entry that
restated the same contract.
Drop the defensive PyUnicode_Check that raised TypeError, and use the
public PyUnicode_EqualToUTF8 instead of _PyUnicode_EqualToASCIIString.
A non-string name now falls through to AttributeError, matching the
existing C-module __getattr__ idiom in _ctypes/callproc.c and
zlibmodule.c.
Switch sys.__getattr__ to METH_VARARGS with PyArg_UnpackTuple, matching
the existing C-module __getattr__ pattern in _ctypes/callproc.c and
zlibmodule.c.
…pshot

Pre-size the temporary dict in _PyImport_GetLazyModulesSnapshot via
_PyDict_NewPresized to avoid resizes during the inline copy. Add a fast
path that returns an empty frozendict directly when the registry has no
entries, skipping the temporary dict allocation entirely.
Drop _PyDict_NewPresized and the empty-registry short-circuit. Both
were micro-optimizations with negligible impact for typical registry
sizes; the simpler unconditional PyDict_New + iterate path is easier
to read.
@read-the-docs-community
Copy link
Copy Markdown

read-the-docs-community Bot commented May 5, 2026

@johnslavik johnslavik marked this pull request as draft May 5, 2026 12:03
johnslavik added 5 commits May 5, 2026 14:13
- Add ``sys.lazy_modules`` entry to Doc/library/sys.rst (frozendict
  snapshot, fresh per access, versionadded 3.15)
- Fix test_builtin.test_vars: sys.lazy_modules is served by
  sys.__getattr__ so it appears in dir() but not vars()
- Fix test_inspect.test_sys_module_has_signatures: sys.__dir__ and
  sys.__getattr__ are METH_NOARGS/METH_VARARGS builtins without
  Argument Clinic text signatures; add them to no_signature
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant